Skip to content

Offer page_token only where GitLab pages by keyset, and gate R-PAGE on it - #1674

Merged
jmrplens merged 6 commits into
issue-1165-ordering-refusalsfrom
issue-1165-offset-lists
Oct 31, 2026
Merged

jmrplens merged 6 commits into
issue-1165-ordering-refusalsfrom
issue-1165-offset-lists

Conversation

@jmrplens

@jmrplens jmrplens commented Oct 31, 2026 •

Copy link
Copy Markdown
Owner

Refs #1165, #1530 and #1600

This is the middle layer of the second #1165 stack. The layer below it made the four project lists refuse keyset outside id order and took the unread ordering off the two job lists. This one takes pagination and page_token off the 182 actions whose routes GitLab pages by offset alone, makes R-PAGE's keyset half a gate so they cannot come back, and rewrites what those lists said about keyset pagination. #1530 and #1600 are part of the 182 and are answered here with their ordering fields. The top of the stack carries the site pages, the contributor pages, row 112 of the upstream bugs and the regeneration.

R-INPUT leaves the keyset fields to R-PAGE

R-INPUT asked of every input whether it carries pagination and page_token, the two keyset parameters client-go declares on gl.ListOptions alone. Answering that for an offset list took one declaration per field, which is about 360 entries once the keyset input leaves those lists. R-PAGE's keyset half asks the same question at the action's grain from the routes the live record says the action sends, in both directions: page-token-on-offset-route for a list that offers it where nothing pages by keyset, and keyset-route-without-page-token for one that does not offer it where GitLab does. So R-INPUT stops asking, and the 34 per-field entries that answered either field are gone. Their ordering halves stay.

The 182 offset lists

GitLab ignores pagination and page_token on these routes, or refuses them, so a caller was offered a way through the list that does not exist. 179 actions lose the toolutil.KeysetPaginationInput embed in the first of the two commits and the three of #1530 and #1600 in the second. Each handler passes a zero KeysetPaginationInput to ApplyListOptions, as #1573 did, and the helpers that carried the keyset input on for an offset list (access requests, award emoji, deploy tokens, Geo, the job token allowlists, the merge request lists, the resource events, the issue and merge request link lists, the runner lists) lose the parameter. The 20 actions on keyset routes keep it. The full list, with each route and the GitLab line that pages it, is at the end.

The two KeysetContinuation calls on offset routes go. groupsshcerts/group_ssh_certs.go and uploads/uploads.go build their query by hand, and the continuation they applied could never be reached.

project.list_user_contributed loses keyset as well. It shared userProjectFilterInput with the owned and starred lists, which GitLab pages by keyset in id order. That input now carries the filters and offset pagination alone, and userProjectFilter adds the keyset half for the two lists that use it, so contributedKeysetRefusal and its run-time check are deleted and the schema refuses both fields instead. The keyset-not-followable declaration of GET /users/:user_id/contributed_projects answered nothing once the action stopped offering page_token, R-PAGE reported it stale, and it is removed. The id_after and id_before descriptions of the project lists no longer call themselves keyset pagination: they are filters.

I did not widen the removal of order_by and sort past the routes #1530 and #1600 name. About 157 actions offer an ordering no route they send declares (audit-1to1-required counts 156 unplaced order_by and 143 unplaced sort here), and that class needs each route read before either field goes, so it is a change of its own.

#1530 and #1600

mr_review.changes_get offered order_by, sort, pagination and page_token. GET /projects/:id/merge_requests/:merge_request_iid/diffs declares page, per_page and unidiff (gitlab-api-live.json, 19.5.0-ee) and pages the latest diff's files with paginated_diffs(params[:page], params[:per_page]) (lib/api/merge_requests.rb:716-722 at v19.5.0-ee), which reads no order. mr_review.diff_versions_list offered the same four on GET .../versions, which declares page and per_page and presents paginate(merge_request.merge_request_diffs.order_id_desc) (lib/api/merge_request_diffs.rb:32-35): newest first whatever is sent. Both inputs lose the four fields, the version list's order_by guidance goes, and the gitlab_mr_review meta description names the parameters each action takes. DiffVersionGetInput already embedded no pagination on main, since #1584.

issue.iteration_list_group offered the same four on GET /groups/:id/iterations, which declares none of them. iterations_finder_params hands IterationsFinder no sort, so it orders by due date, title and id whatever is sent (ee/lib/api/iterations.rb:53-73 and ee/app/finders/iterations_finder.rb:116-123 at v19.5.0-ee), and list_iterations_for pages by offset. The input loses the four fields.

R-INPUT answers the six ordering fields with three cited reasons in acceptedMissingInputs, mrChangesUnordered, mrDiffVersionsOrderedByID and groupIterationsUnordered, in the style of projectIterationsOffsetOnly.

R-PAGE's keyset half gates

It reported and did not gate, because what it found against the 19.5.0 record was the 182, and taking them off was a change of the published surface. With that change made, Summary.clean counts KeysetFindingsUndeclared: an action that offers page_token where no route it sends pages by keyset, or a keyset list that does not offer it, fails the run until its input changes or a route declaration says why it cannot. A re-recorded live record that moves a route from one pager to the other is held to the same rule, which matters for the 19.6 re-pin. On this branch R-PAGE counts 20 actions offering page_token, all 20 on keyset routes, none on an offset route, and four lists that send a keyset route without offering page_token, each on a route declared keyset-not-followable: group.list and group.search on GET /groups, which pages by keyset only for an anonymous caller, and the two service account token lists.

What the lists say

Taking the fields off left the texts behind. 112 actions still said "offset or keyset pagination" in their usage line or description, called their order_by the "column to order keyset-paginated results by", told a caller order_by "only applies when pagination='keyset'", or offered "order_by/sort with pagination='keyset'". A model that reads that asks for pagination=keyset. Each text now says what the list does: the usage lines and descriptions name offset pagination, and the ordering descriptions and guidance name the column and the direction and nothing about a pager. They stay neutral on whether GitLab applies the ordering, which is the class above. The inputs' doc comments say why they carry no keyset fields, applyKeysetOrder in projects becomes applyListOrder, and the KeysetPaginationInput doc says to embed it only where the route pages by keyset.

Coverage of the touched packages

groupepicboards, mrdiscussions and mrdraftnotes stood below 100% of statements on main, for branches no test reached. validatePosition in the two note packages skips the line check for a file GitLab lists with an empty diff, and a test now names a line no diff holds on such a file. The two raw epic board helpers escaped the group into the path themselves, so their request-building error could not be reached; they now take the path from their handlers, as the deployments helpers do, and a test passes one with an invalid percent-escape. The routes, the sites the request record names and what the handlers send are unchanged.

How I tested it

Each new test failed on its parent and passes here. TestDiffPair_TheKeysetFieldsOfListOptions_AreLeftToRPAGE reported page_token and pagination missing and neither fixture declaration stale. TestDiffLists_OrderingAndKeysetArguments_AreNotOffered and TestDiffLists_OrderingAndKeysetArguments_AreRefusedBeforeAnyRequest failed for each of the four fields of both diff lists, the second with every request reaching the mock that forbids one, and TestList_Schema_OffersNoOrderingOrKeysetArgument for each of the four fields of the iteration list. TestRun_AnUndeclaredKeysetFinding_FailsTheGate failed for both undeclared shapes, which Run reported clean, and with the gate in place TestRun_TheRealTree_PassesItsOwnGate fails on main with 182 undeclared findings. TestBuildActionCatalog_OffsetLists_ClaimNoKeysetPagination holds every action that takes page and offers no page_token to its texts sentence by sentence, allowing a sentence that denies keyset; on its parent it failed for 112 actions with 218 claims on each instance. The unit tests that asserted pagination=keyset and page_token reached an offset route now assert the ordering and offset parameters alone, and the ones that only followed a keyset page on an offset route are deleted.

audit-1to1-required counts 17 unplaced pagination and 15 unplaced page_token, all of them on the keyset lists, where it counted 199 and 197 on main.

Every package this layer touches is at 100% of statements, 102 of them. gobco measures every condition outcome of the hand-changed packages: 94 of 94 in mrchanges, 4 of 4 in groupiterations, 48 of 48 in groupsshcerts, 68 of 68 in uploads, 60 of 60 in groupepicboards, 152 of 152 in mrdiscussions, 158 of 158 in mrdraftnotes, 1062 of 1062 in cmd/audit_1to1/internal/paths and 1404 of 1404 in projects, and 919 of 920 in cmd/audit_1to1/internal/structs, where the outcome left is the one the layer below described, len(ret.Results) == 0 being false in cross_call.go, which no type-checked return reaches. gremlins leaves no mutant lived or timed out in the files this layer changes by hand: 481 killed in projects.go, 170 in structs' analyze.go, 32 in paths' analyze.go and pagination_keyset.go, 12 in the functions of pagination_declarations.go, 34 in mr_changes.go, 28 in uploads.go, 22 in group_ssh_certs.go, 14 in group_epic_boards.go and 2 in group_iterations.go. gremlins also lists 32 mutants of pagination_declarations.go as not covered: they are the + joining the string literals of the declaration table's reasons, a package-level initializer that a coverage profile never counts, and none of them is a line this layer adds. For the roughly 100 packages whose change is the embed and the zero value, the package tests and the schemas are the evidence: those lines carry nothing a mutant can change.

Every commit builds and passes go test ./... -count=1 with GITLAB_MCP_TEST_SNAPSHOT_PARITY=deferred. golangci-lint with the e2e tag and golangci-lint fmt --diff are clean on the changed packages, and these gates pass: audit-1to1 (with its SDK, R-PATH, R-GRANT and requiredness steps), audit-1to1-paths, audit-1to1-required, check-action-grants-derivation, check-action-grants, audit-1to1-grants, surface quality, meta descriptions, action IDs, gateway characters, e2e static, case loops, test goroutines, test file names, documentation tool names, godoc, SDK context, Markdown escaping, dead constants, file headers, DCO and the em dash check.

The tool snapshots, the tool reference, llms-full.txt, the token footprint and the request inventory move with these schemas and are regenerated once at the top of the stack, as the deferred parity allows below it. No end-to-end run belongs to this layer: the top runs the complete CE and EE suites with scenarios for the removed shapes.

The 182 actions, their routes and the GitLab 19.5.0 line that pages them
Action Route GitLab 19.5.0 How GitLab answers
access.deploy_key_list_all GET /deploy_keys lib/api/deploy_keys.rb:48 offset paginate
access.deploy_key_list_project GET /projects/:id/deploy_keys lib/api/deploy_keys.rb:104 offset paginate
access.deploy_key_list_user_project GET /users/:user_id/project_deploy_keys lib/api/users.rb:597 offset paginate
access.deploy_token_list_group GET /groups/:id/deploy_tokens lib/api/deploy_tokens.rb:199 offset paginate
access.deploy_token_list_project GET /projects/:id/deploy_tokens lib/api/deploy_tokens.rb:87 offset paginate
access.invite_list_group GET /groups/:id/invitations lib/api/invitations.rb:86 offset paginate
access.invite_list_project GET /projects/:id/invitations lib/api/invitations.rb:86 offset paginate
access.request_list_group GET /groups/:id/access_requests lib/api/access_requests.rb:33 offset paginate
access.request_list_project GET /projects/:id/access_requests lib/api/access_requests.rb:33 offset paginate
access.token_group_list GET /groups/:id/access_tokens lib/api/resource_access_tokens.rb:47 offset paginate
access.token_personal_list GET /personal_access_tokens lib/api/personal_access_tokens.rb:42 offset paginate
access.token_project_list GET /projects/:id/access_tokens lib/api/resource_access_tokens.rb:47 offset paginate
admin.alert_metric_image_list GET /projects/:id/alert_management_alerts/:alert_iid/metric_images lib/api/alert_management_alerts.rb:96 unpaginated: every metric image in one response
admin.application_list GET /applications lib/api/applications.rb:64 unpaginated: every application in one response
admin.broadcast_message_list GET /broadcast_messages lib/api/admin/broadcast_messages.rb:30 offset paginate
admin.bulk_import_entity_list GET /bulk_imports/:import_id/entities and GET /bulk_imports/entities lib/api/bulk_imports.rb:172 offset paginate
admin.bulk_import_list GET /bulk_imports lib/api/bulk_imports.rb:144 offset paginate
admin.cluster_agent_list GET /projects/:id/cluster_agents lib/api/clusters/agents.rb:32 offset paginate
admin.cluster_agent_token_list GET /projects/:id/cluster_agents/:agent_id/tokens lib/api/clusters/agent_tokens.rb:35 offset paginate
admin.error_tracking_list GET /projects/:id/error_tracking/client_keys lib/api/error_tracking/client_keys.rb:32 offset paginate
admin.secure_file_list GET /projects/:id/secure_files lib/api/ci/secure_files.rb:37 offset paginate
admin.topic_list GET /topics lib/api/topics.rb:42 offset paginate
branch.list_protected GET /projects/:id/protected_branches lib/api/protected_branches.rb:46 offset paginate
ci_variable.group_list GET /groups/:id/variables lib/api/group_variables.rb:33 offset paginate
ci_variable.instance_list GET /admin/ci/variables lib/api/admin/ci/variables.rb:30 offset paginate
ci_variable.list GET /projects/:id/variables lib/api/ci/variables.rb:35 offset paginate
dependency.list GET /projects/:id/dependencies ee/lib/api/dependencies.rb:58 offset paginate
enterprise_user.list GET /groups/:id/enterprise_users ee/lib/api/group_enterprise_users.rb:68 offset paginate
environment.deployment_list GET /projects/:id/deployments lib/api/deployments.rb:84 offset paginate
environment.deployment_merge_requests GET /projects/:id/deployments/:deployment_id/merge_requests lib/api/deployments.rb:279 offset paginate
environment.freeze_list GET /projects/:id/freeze_periods lib/api/freeze_periods.rb:40 offset paginate
environment.list GET /projects/:id/environments lib/api/environments.rb:53 offset paginate
environment.protected_list GET /projects/:id/protected_environments ee/lib/api/protected_environments.rb:118 offset paginate
external_status_check.list_project GET /projects/:id/external_status_checks ee/lib/api/status_checks.rb:71 offset paginate
external_status_check.list_project_checks GET /projects/:id/external_status_checks ee/lib/api/status_checks.rb:71 offset paginate
external_status_check.list_project_mr_checks GET /projects/:id/merge_requests/:merge_request_iid/status_checks ee/lib/api/status_checks.rb:216 offset paginate
feature_flags.feature_flag_list GET /projects/:id/feature_flags lib/api/feature_flags.rb:47 offset paginate
feature_flags.ff_user_list_list GET /projects/:id/feature_flags_user_lists lib/api/feature_flags_user_lists.rb:42 offset paginate
geo.list GET /geo_sites ee/lib/api/geo_sites.rb:128 offset paginate
geo.list_status GET /geo_sites/status ee/lib/api/geo_sites.rb:152 offset paginate
group.badge_list GET /groups/:id/badges lib/api/badges.rb:56 offset paginate
group.credential_list_pats GET /groups/:id/manage/personal_access_tokens ee/lib/api/manage/groups.rb:87 offset paginate
group.credential_list_ssh_keys GET /groups/:id/manage/ssh_keys ee/lib/api/manage/groups.rb:258 offset paginate
group.epic_board_list GET /groups/:id/epic_boards ee/lib/api/epic_boards.rb:54 offset paginate
group.epic_list GET /groups/:id/epics ee/lib/api/epics.rb:137 offset paginate
group.event_epic_label_list GET /groups/:id/epics/:eventable_id/resource_label_events lib/api/resource_label_events.rb:15 offset paginate in present_resource_label_event_collection
group.get GET /groups/:id lib/api/groups.rb:456 a single group: nothing to page
group.group_billable_member_memberships_list GET /groups/:id/billable_members/:user_id/memberships ee/lib/ee/api/members.rb:228 offset paginate
group.group_billable_members_list GET /groups/:id/billable_members ee/lib/ee/api/members.rb:162 offset paginate
group.group_board_list GET /groups/:id/boards lib/api/group_boards.rb:37 offset paginate
group.group_board_list_lists GET /groups/:id/boards/:board_id/lists lib/api/group_boards.rb:86 offset paginate
group.group_label_list GET /groups/:id/labels lib/api/helpers/label_helpers.rb:44 offset paginate in get_labels
group.group_milestone_issues GET /groups/:id/milestones and GET /groups/:id/milestones/:milestone_id/issues lib/api/milestone_responses.rb:91 offset paginate in milestone_issuables_for
group.group_milestone_list GET /groups/:id/milestones lib/api/milestone_responses.rb:52 offset paginate in list_milestones_for
group.group_milestone_merge_requests GET /groups/:id/milestones and GET /groups/:id/milestones/:milestone_id/merge_requests lib/api/milestone_responses.rb:91 offset paginate in milestone_issuables_for
group.group_relations_list_status GET /groups/:id/export_relations/status lib/api/group_export.rb:206 unpaginated: every export status in one response
group.group_upload_list GET /groups/:id/uploads lib/api/markdown_uploads.rb:278 offset paginate
group.hook_list GET /groups/:id/hooks ee/lib/api/group_hooks.rb:84 offset paginate
group.invited_groups GET /groups/:id/invited_groups lib/api/groups.rb:152 offset paginate in present_groups
group.issues GET /groups/:id/issues lib/api/issues.rb:218 offset paginate
group.list GET /groups lib/api/groups.rb:171 an authenticated caller (always, here) is presented through present_groups and offset paginate; only an anonymous request reaches paginate_with_strategies
group.list_provisioned_users GET /groups/:id/provisioned_users ee/lib/ee/api/groups.rb:342 offset paginate
group.members GET /groups/:id/members/all lib/api/members.rb:76 offset paginate
group.projects GET /groups/:id/projects lib/api/groups.rb:128 offset paginate in find_group_projects
group.protected_branch_list GET /groups/:id/protected_branches ee/lib/api/group_protected_branches.rb:53 offset paginate
group.protected_env_list GET /groups/:id/protected_environments ee/lib/api/protected_environments.rb:260 offset paginate
group.release_list GET /groups/:id/releases lib/api/releases.rb:468 offset paginate in present_group_releases
group.saml_users_list GET /groups/:id/saml_users ee/lib/ee/api/groups.rb:312 offset paginate
group.shared_projects GET /groups/:id/projects/shared lib/api/groups.rb:128 offset paginate in find_group_projects
group.shared_with GET /groups/:id/groups/shared lib/api/groups.rb:152 offset paginate in present_groups
group.ssh_cert_list GET /groups/:id/ssh_certificates ee/lib/ee/api/groups.rb:369 offset paginate
group.subgroups GET /groups/:id/descendant_groups lib/api/groups.rb:152 offset paginate in present_groups
group.transfer_locations GET /groups/:id/transfer_locations lib/api/groups.rb:152 offset paginate in present_groups
issue.discussion_list GET /projects/:id/issues/:noteable_id/discussions lib/api/discussions.rb:60 offset paginate
issue.emoji_issue_list GET /projects/:id/issues/:issue_iid/award_emoji lib/api/award_emoji.rb:69 offset paginate
issue.emoji_issue_note_list GET /projects/:id/issues/:issue_iid/notes/:note_id/award_emoji lib/api/award_emoji.rb:69 offset paginate
issue.event_issue_iteration_list GET /projects/:id/issues/:eventable_id/resource_iteration_events ee/lib/api/resource_iteration_events.rb:40 offset paginate
issue.event_issue_label_list GET /projects/:id/issues/:eventable_id/resource_label_events lib/api/resource_label_events.rb:15 offset paginate in present_resource_label_event_collection
issue.event_issue_milestone_list GET /projects/:id/issues/:eventable_id/resource_milestone_events lib/api/resource_milestone_events.rb:44 offset paginate
issue.event_issue_state_list GET /projects/:id/issues/:eventable_id/resource_state_events lib/api/resource_state_events.rb:42 offset paginate
issue.event_issue_weight_list GET /projects/:id/issues/:eventable_id/resource_weight_events ee/lib/api/resource_weight_events.rb:42 offset paginate
issue.iteration_list_group GET /groups/:id/iterations ee/lib/api/iterations.rb:56 offset paginate in list_iterations_for
issue.list_all GET /issues lib/api/issues.rb:166 offset paginate
issue.list_group GET /groups/:id/issues lib/api/issues.rb:218 offset paginate
issue.mrs_closing GET /projects/:id/issues/:issue_iid/closed_by lib/api/issues.rb:601 offset paginate
issue.mrs_related GET /projects/:id/issues/:issue_iid/related_merge_requests lib/api/issues.rb:576 offset paginate
issue.note_list GET /projects/:id/issues/:noteable_id/notes lib/api/notes.rb:87 offset paginate
job.list GET /projects/:id/pipelines/:pipeline_id/jobs lib/api/ci/pipelines.rb:216 offset paginate
job.token_scope_list_groups GET /projects/:id/job_token_scope/groups_allowlist lib/api/project_job_token_scope.rb:108 offset paginate
job.token_scope_list_inbound GET /projects/:id/job_token_scope/allowlist lib/api/project_job_token_scope.rb:85 offset paginate
merge_request.commits GET /projects/:id/merge_requests/:merge_request_iid/commits lib/api/merge_requests.rb:574 offset paginate
merge_request.emoji_mr_list GET /projects/:id/merge_requests/:merge_request_iid/award_emoji lib/api/award_emoji.rb:69 offset paginate
merge_request.emoji_mr_note_list GET /projects/:id/merge_requests/:merge_request_iid/notes/:note_id/award_emoji lib/api/award_emoji.rb:69 offset paginate
merge_request.event_mr_label_list GET /projects/:id/merge_requests/:eventable_id/resource_label_events lib/api/resource_label_events.rb:15 offset paginate in present_resource_label_event_collection
merge_request.event_mr_milestone_list GET /projects/:id/merge_requests/:eventable_id/resource_milestone_events lib/api/resource_milestone_events.rb:44 offset paginate
merge_request.event_mr_state_list GET /projects/:id/merge_requests/:eventable_id/resource_state_events lib/api/resource_state_events.rb:42 offset paginate
merge_request.issues_closed GET /projects/:id/merge_requests/:merge_request_iid/closes_issues lib/api/merge_requests.rb:1034 offset paginate
merge_request.list GET /projects/:id/merge_requests lib/api/merge_requests.rb:109 offset paginate in find_merge_requests
merge_request.list_global GET /merge_requests lib/api/merge_requests.rb:109 offset paginate in find_merge_requests
merge_request.list_group GET /groups/:id/merge_requests lib/api/merge_requests.rb:109 offset paginate in find_merge_requests
merge_request.related_issues GET /projects/:id/merge_requests/:merge_request_iid/related_issues lib/api/merge_requests.rb:1061 offset paginate
merge_train.list_branch GET /projects/:id/merge_trains/:target_branch ee/lib/api/merge_trains.rb:84 offset paginate
merge_train.list_project GET /projects/:id/merge_trains ee/lib/api/merge_trains.rb:48 offset paginate
mr_review.changes_get GET /projects/:id/merge_requests/:merge_request_iid/diffs lib/api/merge_requests.rb:719 offset paginate
mr_review.diff_versions_list GET /projects/:id/merge_requests/:merge_request_iid/versions lib/api/merge_request_diffs.rb:35 offset paginate
mr_review.discussion_list GET /projects/:id/merge_requests/:noteable_id/discussions lib/api/discussions.rb:60 offset paginate
mr_review.draft_note_list GET /projects/:id/merge_requests/:merge_request_iid/draft_notes lib/api/draft_notes.rb:140 unpaginated: every draft note in one response
mr_review.note_list GET /projects/:id/merge_requests/:noteable_id/notes lib/api/notes.rb:87 offset paginate
package.file_list GET /projects/:id/packages/:package_id/package_files lib/api/package_files.rb:65 offset paginate
package.group_list GET /groups/:id/packages lib/api/group_packages.rb:87 offset paginate
package.list GET /projects/:id/packages lib/api/project_packages.rb:67 offset paginate
package.protection_rule_list GET /projects/:id/packages/protection/rules lib/api/project_packages_protection_rules.rb:31 unpaginated: every rule in one response
package.registry_list_group GET /groups/:id/registry/repositories lib/api/group_container_repositories.rb:46 offset paginate
package.registry_list_project GET /projects/:id/registry/repositories lib/api/project_container_repositories.rb:50 offset paginate
pipeline.latest GET /projects/:id and GET /projects/:id/pipelines and GET /projects/:id/pipelines/:pipeline_id and GET /projects/:id/pipelines/latest and GET /projects/:id/repository/commits/:sha lib/api/ci/pipelines.rb:90 offset paginate
pipeline.list GET /projects/:id/pipelines lib/api/ci/pipelines.rb:90 offset paginate
pipeline.schedule_list GET /projects/:id/pipeline_schedules lib/api/ci/pipeline_schedules.rb:43 offset paginate
pipeline.schedule_list_triggered_pipelines GET /projects/:id/pipeline_schedules/:pipeline_schedule_id/pipelines lib/api/ci/pipeline_schedules.rb:107 offset paginate
pipeline.trigger_list GET /projects/:id/triggers lib/api/ci/triggers.rb:92 offset paginate
project.approval_rule_list GET /projects/:id/approval_rules ee/lib/api/project_approval_rules.rb:32 offset paginate
project.badge_list GET /projects/:id/badges lib/api/badges.rb:56 offset paginate
project.board_list GET /projects/:id/boards lib/api/boards.rb:37 offset paginate
project.board_list_list GET /projects/:id/boards/:board_id/lists lib/api/boards.rb:116 offset paginate
project.hook_list GET /projects/:id/hooks lib/api/project_hooks.rb:92 offset paginate
project.label_list GET /projects/:id/labels lib/api/helpers/label_helpers.rb:44 offset paginate in get_labels
project.list_groups GET /projects/:id/groups lib/api/projects.rb:273 offset paginate in present_groups
project.list_invited_groups GET /projects/:id/invited_groups lib/api/projects.rb:273 offset paginate in present_groups
project.list_starrers GET /projects/:id/starrers lib/api/projects.rb:896 offset paginate
project.list_user_contributed GET /users/:user_id/contributed_projects lib/api/projects.rb:396 keyset page whose next link is never applied (upstream-bugs row 112)
project.list_users GET /projects/:id/users lib/api/projects.rb:1114 offset paginate
project.members GET /projects/:id/members/all lib/api/members.rb:76 offset paginate
project.milestone_issues GET /projects/:id/milestones and GET /projects/:id/milestones/:milestone_id/issues lib/api/milestone_responses.rb:91 offset paginate in milestone_issuables_for
project.milestone_list GET /projects/:id/milestones lib/api/milestone_responses.rb:52 offset paginate in list_milestones_for
project.milestone_merge_requests GET /projects/:id/milestones and GET /projects/:id/milestones/:milestone_id/merge_requests lib/api/milestone_responses.rb:91 offset paginate in milestone_issuables_for
project.mirror_list GET /projects/:id/remote_mirrors lib/api/remote_mirrors.rb:53 offset paginate
project.pages_domain_list GET /projects/:id/pages/domains lib/api/pages_domains.rb:89 offset paginate
project.upload_list GET /projects/:id/uploads lib/api/markdown_uploads.rb:100 offset paginate
release.link_list GET /projects/:id/releases/:tag_name/assets/links lib/api/release/links.rb:46 offset paginate
release.list GET /projects/:id/releases lib/api/releases.rb:113 offset paginate
repository.commit_comments GET /projects/:id/repository/commits/:sha/comments lib/api/commits.rb:465 offset paginate
repository.commit_diff GET /projects/:id/repository/commits/:sha/diff lib/api/commits.rb:444 offset paginate
repository.commit_discussion_list GET /projects/:id/repository/commits/:noteable_id/discussions lib/api/discussions.rb:60 offset paginate
repository.commit_refs GET /projects/:id/repository/commits/:sha/refs lib/api/commits.rb:657 offset paginate
repository.commit_statuses GET /projects/:id/repository/commits/:sha/statuses lib/api/commit_statuses.rb:70 offset paginate
repository.contributors GET /projects/:id/repository/contributors lib/api/repositories.rb:388 offset paginate
runner.controller_list GET /runner_controllers ee/lib/api/ci/runner_controllers.rb:36 offset paginate
runner.controller_token_list GET /runner_controllers/:runner_controller_id/tokens ee/lib/api/ci/runner_controller_tokens.rb:41 offset paginate
runner.list GET /runners lib/api/ci/runners.rb:160 offset paginate
runner.list_all GET /runners/all lib/api/ci/runners.rb:182 offset paginate
runner.list_group GET /groups/:id/runners lib/api/ci/runners.rb:488 offset paginate
runner.list_project GET /projects/:id/runners lib/api/ci/runners.rb:414 offset paginate
snippet.discussion_list GET /projects/:id/snippets/:noteable_id/discussions lib/api/discussions.rb:60 offset paginate
snippet.emoji_snippet_list GET /projects/:id/snippets/:snippet_id/award_emoji lib/api/award_emoji.rb:69 offset paginate
snippet.emoji_snippet_note_list GET /projects/:id/snippets/:snippet_id/notes/:note_id/award_emoji lib/api/award_emoji.rb:69 offset paginate
snippet.explore GET /snippets/public lib/api/snippets.rb:75 offset paginate
snippet.list GET /snippets lib/api/snippets.rb:50 offset paginate
snippet.list_all GET /snippets/all lib/api/snippets.rb:105 offset paginate
snippet.note_list GET /projects/:id/snippets/:noteable_id/notes lib/api/notes.rb:87 offset paginate
snippet.project_list GET /projects/:id/snippets lib/api/project_snippets.rb:53 offset paginate
storage_move.retrieve_all_group GET /group_repository_storage_moves ee/lib/api/group_repository_storage_moves.rb:26 offset paginate
storage_move.retrieve_all_project GET /project_repository_storage_moves lib/api/project_repository_storage_moves.rb:36 offset paginate
storage_move.retrieve_all_snippet GET /snippet_repository_storage_moves lib/api/snippet_repository_storage_moves.rb:27 offset paginate
storage_move.retrieve_group GET /groups/:id/repository_storage_moves ee/lib/api/group_repository_storage_moves.rb:86 offset paginate
storage_move.retrieve_project GET /projects/:id/repository_storage_moves lib/api/project_repository_storage_moves.rb:99 offset paginate
storage_move.retrieve_snippet GET /snippets/:id/repository_storage_moves lib/api/snippet_repository_storage_moves.rb:100 offset paginate
tag.list_protected GET /projects/:id/protected_tags lib/api/protected_tags.rb:36 offset paginate
template.ci_yml_list GET /templates/gitlab_ci_ymls lib/api/templates.rb:92 offset paginate
template.dockerfile_list GET /templates/dockerfiles lib/api/templates.rb:92 offset paginate
template.gitignore_list GET /templates/gitignores lib/api/templates.rb:92 offset paginate
template.license_list GET /templates/licenses lib/api/templates.rb:49 offset paginate
template.project_template_list GET /projects/:id/templates/:type lib/api/project_templates.rb:49 offset paginate
user.activities GET /user/activities lib/api/users.rb:1790 offset paginate
user.contribution_events GET /projects/:id and GET /users/:id/events lib/api/helpers/events_helpers.rb:21 offset paginate in present_events
user.emails_for_user GET /users/:id/emails lib/api/users.rb:876 offset paginate
user.event_list_contributions GET /events and GET /projects/:id lib/api/helpers/events_helpers.rb:21 offset paginate in present_events
user.event_list_project GET /projects/:id and GET /projects/:id/events lib/api/helpers/events_helpers.rb:21 offset paginate in present_events
user.list_impersonation_tokens GET /users/:user_id/impersonation_tokens lib/api/users.rb:1219 offset paginate
user.memberships GET /users/:user_id/memberships lib/api/users.rb:1154 offset paginate
user.namespace_list GET /namespaces lib/api/namespaces.rb:59 offset paginate
user.ssh_keys GET /user/keys lib/api/users.rb:1410 offset paginate
user.ssh_keys_for_user GET /users/:user_id/keys lib/api/users.rb:650 offset paginate
user.todo_list GET /todos lib/api/todos.rb:116 offset paginate

@sourcery-ai

sourcery-ai Bot commented Oct 31, 2026

Copy link
Copy Markdown

Sorry @jmrplens, your pull request is larger than the review limit of 150,000 diff characters

@jmrplens
jmrplens added this pull request to stack #1676 October 31, 2026 17:44
@coderabbitai

coderabbitai Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 262 files, which is 162 over the limit of 100.

To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch.

Upgrade to a paid plan to raise the limit.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Check out review usage here.

Run configuration
Commits

Reviewing files that changed from the base of the PR and between ed890b3 and acadca3.

Files selected for processing: 262
  • cmd/audit_1to1/internal/paths/analyze.go
  • cmd/audit_1to1/internal/paths/pagination_declarations.go
  • cmd/audit_1to1/internal/paths/pagination_keyset.go
  • cmd/audit_1to1/internal/paths/pagination_keyset_test.go
  • cmd/audit_1to1/internal/structs/analyze.go
  • cmd/audit_1to1/internal/structs/analyze_test.go
  • internal/tools/accessrequests/access_requests.go
  • internal/tools/accessrequests/access_requests_test.go
  • internal/tools/accessrequests/action_specs.go
  • internal/tools/accesstokens/accesstokens.go
  • internal/tools/accesstokens/accesstokens_test.go
  • internal/tools/action_catalog_test.go
  • internal/tools/alertmanagement/alert_management.go
  • internal/tools/alertmanagement/alert_management_test.go
  • internal/tools/applications/applications.go
  • internal/tools/applications/applications_test.go
  • internal/tools/awardemoji/award_emoji.go
  • internal/tools/awardemoji/award_emoji_test.go
  • internal/tools/badges/badges.go
  • internal/tools/badges/badges_test.go
  • internal/tools/boards/boards.go
  • internal/tools/boards/boards_test.go
  • internal/tools/branches/action_specs.go
  • internal/tools/branches/branches.go
  • internal/tools/branches/branches_test.go
  • internal/tools/broadcastmessages/broadcast_messages.go
  • internal/tools/broadcastmessages/broadcast_messages_test.go
  • internal/tools/bulkimports/bulk_imports.go
  • internal/tools/bulkimports/bulk_imports_test.go
  • internal/tools/civariables/action_specs.go
  • internal/tools/civariables/ci_variables.go
  • internal/tools/civariables/ci_variables_test.go
  • internal/tools/ciyamltemplates/ci_yaml_templates.go
  • internal/tools/ciyamltemplates/ci_yaml_templates_test.go
  • internal/tools/clusteragents/cluster_agents.go
  • internal/tools/clusteragents/cluster_agents_test.go
  • internal/tools/commitdiscussions/action_specs.go
  • internal/tools/commitdiscussions/commit_discussions.go
  • internal/tools/commitdiscussions/commit_discussions_test.go
  • internal/tools/commits/action_specs.go
  • internal/tools/commits/commits.go
  • internal/tools/commits/commits_test.go
  • internal/tools/containerregistry/container_registry.go
  • internal/tools/containerregistry/container_registry_test.go
  • internal/tools/dependencies/dependencies.go
  • internal/tools/dependencies/dependencies_test.go
  • internal/tools/deploykeys/deploy_keys.go
  • internal/tools/deploykeys/deploy_keys_test.go
  • internal/tools/deploymentmergerequests/action_specs.go
  • internal/tools/deploymentmergerequests/deployment_merge_requests.go
  • internal/tools/deploymentmergerequests/deployment_merge_requests_test.go
  • internal/tools/deployments/action_specs.go
  • internal/tools/deployments/deployments.go
  • internal/tools/deploytokens/deploy_tokens.go
  • internal/tools/deploytokens/deploy_tokens_test.go
  • internal/tools/dockerfiletemplates/dockerfile_templates.go
  • internal/tools/dockerfiletemplates/dockerfile_templates_test.go
  • internal/tools/enterpriseusers/action_specs.go
  • internal/tools/enterpriseusers/enterprise_users.go
  • internal/tools/enterpriseusers/enterprise_users_test.go
  • internal/tools/environments/environments.go
  • internal/tools/environments/environments_test.go
  • internal/tools/epics/epics.go
  • internal/tools/epics/epics_test.go
  • internal/tools/errortracking/error_tracking.go
  • internal/tools/errortracking/error_tracking_test.go
  • internal/tools/events/action_specs.go
  • internal/tools/events/events.go
  • internal/tools/events/events_test.go
  • internal/tools/externalstatuschecks/action_specs.go
  • internal/tools/externalstatuschecks/external_status_checks.go
  • internal/tools/externalstatuschecks/external_status_checks_test.go
  • internal/tools/featureflags/action_specs.go
  • internal/tools/featureflags/feature_flags.go
  • internal/tools/featureflags/feature_flags_test.go
  • internal/tools/ffuserlists/action_specs.go
  • internal/tools/ffuserlists/ff_user_lists.go
  • internal/tools/ffuserlists/ff_user_lists_test.go
  • internal/tools/freezeperiods/freeze_periods.go
  • internal/tools/freezeperiods/freeze_periods_test.go
  • internal/tools/geo/action_specs.go
  • internal/tools/geo/geo.go
  • internal/tools/geo/geo_test.go
  • internal/tools/gitignoretemplates/action_specs.go
  • internal/tools/gitignoretemplates/gitignore_templates.go
  • internal/tools/gitignoretemplates/gitignore_templates_test.go
  • internal/tools/groupboards/group_boards.go
  • internal/tools/groupboards/group_boards_test.go
  • internal/tools/groupcredentials/group_credentials.go
  • internal/tools/groupcredentials/group_credentials_test.go
  • internal/tools/groupepicboards/group_epic_boards.go
  • internal/tools/groupepicboards/group_epic_boards_test.go
  • internal/tools/groupiterations/action_specs.go
  • internal/tools/groupiterations/group_iterations.go
  • internal/tools/groupiterations/group_iterations_test.go
  • internal/tools/grouplabels/group_labels.go
  • internal/tools/grouplabels/group_labels_test.go
  • internal/tools/groupmarkdownuploads/group_markdown_uploads.go
  • internal/tools/groupmarkdownuploads/group_markdown_uploads_test.go
  • internal/tools/groupmembers/group_members.go
  • internal/tools/groupmilestones/group_milestones.go
  • internal/tools/groupmilestones/group_milestones_test.go
  • internal/tools/groupprotectedbranches/action_specs.go
  • internal/tools/groupprotectedbranches/group_protected_branches.go
  • internal/tools/groupprotectedbranches/group_protected_branches_test.go
  • internal/tools/groupprotectedenvs/action_specs.go
  • internal/tools/groupprotectedenvs/group_protected_envs.go
  • internal/tools/groupprotectedenvs/group_protected_envs_test.go
  • internal/tools/grouprelationsexport/group_relations_export.go
  • internal/tools/grouprelationsexport/group_relations_export_test.go
  • internal/tools/groupreleases/action_specs.go
  • internal/tools/groupreleases/group_releases.go
  • internal/tools/groupreleases/group_releases_test.go
  • internal/tools/groups/groups.go
  • internal/tools/groups/groups_test.go
  • internal/tools/groups/hooks.go
  • internal/tools/groups/hooks_test.go
  • internal/tools/groups/provisioned_users.go
  • internal/tools/groups/sharing.go
  • internal/tools/groupsaml/action_specs.go
  • internal/tools/groupsaml/group_saml.go
  • internal/tools/groupsaml/group_saml_test.go
  • internal/tools/groupsshcerts/action_specs.go
  • internal/tools/groupsshcerts/group_ssh_certs.go
  • internal/tools/groupsshcerts/group_ssh_certs_test.go
  • internal/tools/groupstoragemoves/group_storage_moves.go
  • internal/tools/groupstoragemoves/group_storage_moves_test.go
  • internal/tools/groupvariables/action_specs.go
  • internal/tools/groupvariables/group_variables.go
  • internal/tools/groupvariables/group_variables_test.go
  • internal/tools/impersonationtokens/action_specs.go
  • internal/tools/impersonationtokens/impersonation_tokens.go
  • internal/tools/impersonationtokens/impersonation_tokens_test.go
  • internal/tools/instancevariables/action_specs.go
  • internal/tools/instancevariables/instance_variables.go
  • internal/tools/instancevariables/instance_variables_test.go
  • internal/tools/invites/invites.go
  • internal/tools/invites/invites_test.go
  • internal/tools/issuediscussions/action_specs.go
  • internal/tools/issuediscussions/issue_discussions.go
  • internal/tools/issuediscussions/issue_discussions_test.go
  • internal/tools/issuenotes/action_specs.go
  • internal/tools/issuenotes/issue_notes.go
  • internal/tools/issuenotes/issue_notes_test.go
  • internal/tools/issues/issues.go
  • internal/tools/issues/issues_test.go
  • internal/tools/jobs/jobs.go
  • internal/tools/jobs/jobs_test.go
  • internal/tools/jobtokenscope/job_token_scope.go
  • internal/tools/jobtokenscope/job_token_scope_test.go
  • internal/tools/labels/action_specs.go
  • internal/tools/labels/labels.go
  • internal/tools/labels/labels_test.go
  • internal/tools/licensetemplates/action_specs.go
  • internal/tools/licensetemplates/action_specs_test.go
  • internal/tools/licensetemplates/license_templates.go
  • internal/tools/licensetemplates/license_templates_test.go
  • internal/tools/members/action_specs.go
  • internal/tools/members/members.go
  • internal/tools/members/members_test.go
  • internal/tools/mergerequests/action_specs.go
  • internal/tools/mergerequests/merge_requests.go
  • internal/tools/mergerequests/merge_requests_test.go
  • internal/tools/mergetrains/merge_trains.go
  • internal/tools/mergetrains/merge_trains_test.go
  • internal/tools/milestones/milestones.go
  • internal/tools/milestones/milestones_test.go
  • internal/tools/mrchanges/action_specs.go
  • internal/tools/mrchanges/mr_changes.go
  • internal/tools/mrchanges/mr_changes_test.go
  • internal/tools/mrdiscussions/action_specs.go
  • internal/tools/mrdiscussions/mr_discussions.go
  • internal/tools/mrdiscussions/mr_discussions_test.go
  • internal/tools/mrdraftnotes/mr_draft_notes.go
  • internal/tools/mrdraftnotes/mr_draft_notes_test.go
  • internal/tools/mrnotes/action_specs.go
  • internal/tools/mrnotes/mr_notes.go
  • internal/tools/mrnotes/mr_notes_test.go
  • internal/tools/namespaces/namespaces.go
  • internal/tools/namespaces/namespaces_test.go
  • internal/tools/packages/packages.go
  • internal/tools/packages/packages_test.go
  • internal/tools/pages/pages.go
  • internal/tools/pages/pages_test.go
  • internal/tools/pipelines/latest.go
  • internal/tools/pipelines/latest_test.go
  • internal/tools/pipelines/markdown.go
  • internal/tools/pipelines/pipelines.go
  • internal/tools/pipelines/pipelines_test.go
  • internal/tools/pipelineschedules/action_specs.go
  • internal/tools/pipelineschedules/markdown.go
  • internal/tools/pipelineschedules/pipeline_schedules.go
  • internal/tools/pipelineschedules/pipeline_schedules_test.go
  • internal/tools/pipelinetriggers/action_specs.go
  • internal/tools/pipelinetriggers/pipeline_triggers.go
  • internal/tools/pipelinetriggers/pipeline_triggers_test.go
  • internal/tools/projectmirrors/action_specs.go
  • internal/tools/projectmirrors/project_mirrors.go
  • internal/tools/projectmirrors/project_mirrors_test.go
  • internal/tools/projects/approvals.go
  • internal/tools/projects/projects.go
  • internal/tools/projects/projects_test.go
  • internal/tools/projectstoragemoves/project_storage_moves.go
  • internal/tools/projectstoragemoves/project_storage_moves_test.go
  • internal/tools/projecttemplates/action_specs.go
  • internal/tools/projecttemplates/project_templates.go
  • internal/tools/projecttemplates/project_templates_test.go
  • internal/tools/protectedenvs/action_specs.go
  • internal/tools/protectedenvs/protected_envs.go
  • internal/tools/protectedenvs/protected_envs_test.go
  • internal/tools/protectedpackages/protected_packages.go
  • internal/tools/protectedpackages/protected_packages_test.go
  • internal/tools/releaselinks/action_specs.go
  • internal/tools/releaselinks/release_links.go
  • internal/tools/releaselinks/release_links_test.go
  • internal/tools/releases/releases.go
  • internal/tools/releases/releases_test.go
  • internal/tools/repository/repository.go
  • internal/tools/repository/repository_test.go
  • internal/tools/resourceevents/resource_events.go
  • internal/tools/resourceevents/shapes_test.go
  • internal/tools/runnercontrollers/action_specs.go
  • internal/tools/runnercontrollers/runner_controllers.go
  • internal/tools/runnercontrollers/runner_controllers_test.go
  • internal/tools/runnercontrollertokens/runner_controller_tokens.go
  • internal/tools/runnercontrollertokens/runner_controller_tokens_test.go
  • internal/tools/runners/runners.go
  • internal/tools/runners/runners_test.go
  • internal/tools/securefiles/secure_files.go
  • internal/tools/securefiles/secure_files_test.go
  • internal/tools/snippetdiscussions/action_specs.go
  • internal/tools/snippetdiscussions/snippet_discussions.go
  • internal/tools/snippetdiscussions/snippet_discussions_test.go
  • internal/tools/snippetnotes/action_specs.go
  • internal/tools/snippetnotes/snippet_notes.go
  • internal/tools/snippetnotes/snippet_notes_test.go
  • internal/tools/snippets/action_specs.go
  • internal/tools/snippets/project_snippets.go
  • internal/tools/snippets/snippets.go
  • internal/tools/snippets/snippets_test.go
  • internal/tools/snippetstoragemoves/snippet_storage_moves.go
  • internal/tools/snippetstoragemoves/snippet_storage_moves_test.go
  • internal/tools/tags/action_specs.go
  • internal/tools/tags/tags.go
  • internal/tools/tags/tags_test.go
  • internal/tools/testdata/tools_meta.json
  • internal/tools/testdata/tools_meta_compact.json
  • internal/tools/testdata/tools_meta_full.json
  • internal/tools/todos/todos.go
  • internal/tools/todos/todos_test.go
  • internal/tools/topics/topics.go
  • internal/tools/topics/topics_test.go
  • internal/tools/uploads/uploads.go
  • internal/tools/uploads/uploads_test.go
  • internal/tools/useremails/action_specs.go
  • internal/tools/useremails/user_emails.go
  • internal/tools/useremails/user_emails_test.go
  • internal/tools/users/user_misc.go
  • internal/tools/users/user_ssh_keys.go
  • internal/tools/users/users.go
  • internal/tools/users/users_test.go
  • internal/toolutil/pagination.go

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Review on demand using usage pricing
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@jmrplens jmrplens added this to the 3.5.0 milestone Oct 31, 2026
@jmrplens jmrplens changed the title issue 1165 offset lists Offer page_token only where GitLab pages by keyset, and gate R-PAGE on it Oct 31, 2026
@github-actions github-actions Bot added tooling The audit and generator commands under cmd/, and the Makefile targets that run them v3.5.0 Targeted at the 3.5.0 release labels Oct 31, 2026
R-INPUT no longer asks whether an input carries pagination and
page_token, the two keyset parameters client-go declares on ListOptions
alone. The keyset half of R-PAGE asks the same question at the action's
grain, from the routes the live record says it sends: page_token offered
where no route can be paged by keyset is one of its findings, and a
keyset route sent without it is the other. Answering it here took one
declaration per field of every list GitLab pages by offset alone, which
is the 360 entries taking the keyset input off those lists would have
needed.

The 34 per-field entries that answered pagination or page_token are
removed, since the rule leaves them nothing to answer; their ordering
halves stay.

TestDiffPair_TheKeysetFieldsOfListOptions_AreLeftToRPAGE failed on the
parent: the input pair reported page_token and pagination missing, and
neither fixture declaration was stale.

Refs #1165

Signed-off-by: José M. Requena Plens <mail@jmrp.io>
Refs #1165

179 actions offered pagination and page_token on a route GitLab pages
by offset alone, because their inputs embedded
toolutil.KeysetPaginationInput whatever their route did. GitLab ignores
both there, so a caller was offered a way through the list that does not
exist. Each input loses the embed, its handler passes a zero
KeysetPaginationInput to ApplyListOptions, and the helpers that carried
the keyset input on for an offset list (access requests, award emoji,
deploy tokens, Geo, the job token allowlists, the merge request lists,
the resource events, the issue and merge request link lists, the runner
lists) lose the parameter. The 20 actions on keyset routes keep it.

The two KeysetContinuation calls on offset routes go: the group SSH
certificate list and the project upload list build their query by hand,
and the continuation they applied could never be reached.

project.list_user_contributed loses keyset as well. It shared
userProjectFilterInput with the owned and starred lists, which GitLab
pages by keyset in id order; the input now carries offset pagination
alone and userProjectFilter adds the keyset half for those two, so
contributedKeysetRefusal and its run-time check are deleted and the
schema refuses both fields instead (upstream-bugs row 112). The
keyset-not-followable declaration of GET
/users/:user_id/contributed_projects answered nothing once the action
stopped offering page_token, so R-PAGE reported it stale; it is
removed.

The id_after and id_before descriptions of the project lists no longer
call themselves keyset pagination: they are filters.

R-PAGE's keyset half reports three findings left after this commit,
mr_review.changes_get, mr_review.diff_versions_list and
issue.iteration_list_group, which the next commit takes with their
ordering fields. The unit tests that asserted pagination=keyset and
page_token reached an offset route now assert the ordering and offset
parameters alone; the ones that only followed a keyset page on an offset
route are deleted. The tool snapshots move with the schemas and are
regenerated at the top of the stack.

Signed-off-by: José M. Requena Plens <mail@jmrp.io>
…eir routes never read

Refs #1165, #1530, #1600

mr_review.changes_get offered order_by, sort, pagination and page_token.
GET /projects/:id/merge_requests/:merge_request_iid/diffs declares page,
per_page and unidiff (gitlab-api-live.json, 19.5.0-ee) and pages the
latest diff's files with paginated_diffs(params[:page],
params[:per_page]) (lib/api/merge_requests.rb:716-722 at v19.5.0-ee),
which reads no order and pages by offset alone. mr_review.diff_versions_list
offered the same four on GET .../versions, which declares page and
per_page and presents paginate(merge_request.merge_request_diffs.order_id_desc)
(lib/api/merge_request_diffs.rb:32-35): newest first whatever is sent.
Both inputs lose the four fields, the order_by guidance of the version
list goes, and the gitlab_mr_review meta description, which the meta
snapshots carry, names the parameters each action takes.

issue.iteration_list_group offered order_by, sort, pagination and
page_token on GET /groups/:id/iterations, which declares none of them.
iterations_finder_params hands IterationsFinder no sort, so it orders by
due date, title and id whatever is sent (ee/lib/api/iterations.rb:53-73
and ee/app/finders/iterations_finder.rb:116-123 at v19.5.0-ee), and
list_iterations_for pages by offset. The input loses the four fields and
the usage line no longer promises keyset sorting.

R-INPUT answers the six ordering fields with three cited reasons in
acceptedMissingInputs, mrChangesUnordered, mrDiffVersionsOrderedByID and
groupIterationsUnordered, in the style of projectIterationsOffsetOnly;
their keyset fields are R-PAGE's.

TestDiffLists_OrderingAndKeysetArguments_AreNotOffered and
TestDiffLists_OrderingAndKeysetArguments_AreRefusedBeforeAnyRequest
failed on the parent for each of the four fields of both lists, and
TestList_Schema_OffersNoOrderingOrKeysetArgument for each of the four
fields of the iteration list.

Signed-off-by: José M. Requena Plens <mail@jmrp.io>
The keyset half of R-PAGE reported and did not gate, because what it
found against the 19.5.0 record was page_token offered on 182 actions
whose routes GitLab pages by offset alone, and taking it off them was a
change of the published surface. The three commits before this one made
that change, so the half now reports nothing undeclared on this tree,
and Summary.clean counts KeysetFindingsUndeclared: an action offering
page_token where no route it sends pages by keyset, or a keyset list
that does not offer it, fails the run until its input changes or a
route declaration says why it cannot. A re-recorded live record that
moves a route from one pager to the other is held to the same rule.

TestRun_AnUndeclaredKeysetFinding_FailsTheGate failed on the parent for
both undeclared shapes, which Run reported clean, and
TestRun_TheRealTree_PassesItsOwnGate fails with 182 undeclared findings
on main once the gate counts them.

Refs #1165

Signed-off-by: José M. Requena Plens <mail@jmrp.io>
The commits before this one took pagination and page_token off every
list GitLab pages by offset alone, but the texts those lists serve kept
offering what had gone: 112 actions still said "offset or keyset
pagination" in their usage line or individual description, called their
order_by the "column to order keyset-paginated results by", told a
caller order_by "only applies when pagination='keyset'", or offered
"order_by/sort with pagination='keyset'". A model that reads that asks
for pagination=keyset, which GitLab ignores or refuses on those routes.

Each of those texts now says what the list does: the usage lines and
descriptions name offset pagination, and the order_by and sort
descriptions and guidance name the column and the direction and nothing
about a pager. They stay neutral on whether GitLab applies the ordering,
since most of those routes declare no order_by or sort; that class is
left to its own change. The doc comments of the inputs say why they
carry no keyset fields, applyKeysetOrder in projects becomes
applyListOrder, and the KeysetPaginationInput doc says to embed it only
where the route pages by keyset.

TestBuildActionCatalog_OffsetLists_ClaimNoKeysetPagination holds every
action that takes page and offers no page_token to it, sentence by
sentence, allowing a sentence that denies keyset ("GitLab does not page
this list by keyset"). On the parent it failed for 112 actions with 218
claims on each instance, and the license template spec test, which pins
that action's usage, description and guidance, failed until its
expectations moved with them.

Refs #1165

Signed-off-by: José M. Requena Plens <mail@jmrp.io>
…s left uncovered

This layer touches the three packages, and each stood below 100% of
statements on main, for branches no test reached rather than for code
that cannot run.

validatePosition in mrdiscussions and mrdraftnotes skips the line check
for a file GitLab lists with an empty diff, as it does for a binary file
or one too large to show. TestValidatePosition_FileWithEmptyDiff_SkipsTheLineCheck
names a line no diff holds on such a file and expects no refusal.

The two raw epic board helpers escaped the group into the path
themselves, so the request-building error they return could not be
reached. They now take the path from their handlers, as the deployments
helpers do, and
TestRawBoardFetch_RequestThatCannotBeBuilt_IsRefusedBeforeAnyRequest
passes a path with an invalid percent-escape to both. The routes, the
sites the request record names and what the handlers send are unchanged,
which check-action-grants-derivation confirms.

The three packages are at 100% of statements.

Refs #1165

Signed-off-by: José M. Requena Plens <mail@jmrp.io>
@jmrplens
jmrplens force-pushed the issue-1165-offset-lists branch from 579af4e to acadca3 Compare October 31, 2026 22:46
@sonarqubecloud

Copy link
Copy Markdown

@jmrplens
jmrplens merged commit 2d8d523 into main Oct 31, 2026
39 of 69 checks passed
@jmrplens
jmrplens deleted the issue-1165-offset-lists branch October 31, 2026 23:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

tooling The audit and generator commands under cmd/, and the Makefile targets that run them v3.5.0 Targeted at the 3.5.0 release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant